#include "IOnvifTool.h"
#include "IOnvifToolClass.h"
#include "hypermap.hpp"
#include "utils.h"
#include <thread>
#include <ctime>
#include <set>
#include <vector>
#include <mutex>
#include <json/json.hpp>

using namespace hyperex::magicMap;
using namespace std;
using nlohmann::json;

class onvifService
{
public:
    onvifService();
    ~onvifService();
    static unsigned int getVersion();
    bool setLogToFile(int nLogLevel, char const *strLogDir, int bAutoDel);
    bool start(PDEVICE_FIND_CALLBACK pDeviceFindCallBack, int bInstallNPF, void *pUserData);
    bool stop();
    bool login(const char *serviceAddr, const char *userName, const char *password,
               char *handleOutBuffer, unsigned int outBufferSize);
    bool sendInquery();
    void setAutoRequestInterval(unsigned int dwInterval);
    bool modifyDeviceNetParam(const char *handle, const ONVIFTOOL_DEV_NET_PARAM *lpNetParam,
                              ONVIFTOOL_DEV_RET_NET_PARAM *lpRetNetParam,
                              unsigned int dwOutBuffSize);
    unsigned int getLastError();
    bool clearup();
    bool getDeviceConfig(const char *handle, unsigned int dwCommand,
                         void *lpInBuffer, unsigned int dwInBuffSize,
                         void *lpOutBuffer, unsigned int dwOutBuffSize);
    bool setDeviceConfig(const char *handle, unsigned int dwCommand,
                         void *lpInBuffer, unsigned int dwInBuffSize,
                         void *lpOutBuffer, unsigned int dwOutBuffSize);
    bool resetPasswd(const char *handle,
                     const ONVIFTOOL_RESET_PARAM *pResetParam,
                     ONVIFTOOL_RET_RESET_PARAM *pRetResetParam);

private:
    static void listenHelloDataCB(string jsonStr, void *pUserData);
    static void listenByeDataCB(string jsonStr, void *pUserData);

private:
    void log(string message, int logLevel = -1);
    void listenHello();
    void listenBye();
    void autoInquery();
    string printDeviceInfo(ONVIFTOOL_DEVICE_INFO *pDeviceInfo_ = nullptr);
    string makeErrStr(string errPrefix, string err);
    int addDevice(const json device, bool manual = false);
    int deleteDevice(const json device);
    int getDevInfomation(string serviceAddr);
    int getServices(string serviceAddr, string serviceNameFind, string &outAddr);
    int getNetworkInterface(string serviceAddr);
    int getNetworkDefaultGateway(string serviceAddr);
    int getNetworkProtocols(string serviceAddr);
    string getVideoSourceConfigToken(string serviceAddr, int channel);
    string getVideoSourceToken(string serviceAddr, string videoSourceConfigToken);
    vector<string> getProfileTokenList(string serviceAddr, string VideoSourceConfigToken);
    int getVideoChannel(string serviceAddr);
    bool getStreamURI(string serviceAddr, ONVIFTOOL_GET_STREAM_PARAM *inBuffer,
                      ONVIFTOOL_RET_GET_URI_PARAM *outBuffer);
    bool getDeviceInfo(string serviceAddr, ONVIFTOOL_RET_GET_DEVICE_INFO_PARAM *outBuffer);
    bool getSnapShotURI(string serviceAddr, ONVIFTOOL_GET_SNAPSHOT_PARAM *inBuffer,
                        ONVIFTOOL_RET_GET_URI_PARAM *outBuffer);
    bool getRecordList(string serviceAddr, ONVIFTOOL_GET_RECORD_LIST_PARAM *inBuffer,
                       ONVIFTOOL_RET_GET_RECORD_LIST_PARAM *outBuffer);
    bool getRecordURI(string serviceAddr, ONVIFTOOL_GET_RECORD_URI_PARAM *inBuffer,
                      ONVIFTOOL_RET_GET_URI_PARAM *outBuffer);
    bool getPresets(string serviceAddr, ONVIFTOOL_PRESET_PARAM *inBuffer,
                    ONVIFTOOL_RET_PRESET_PARAM *outBuffer);
    bool setChannelName(string serviceAddr, ONVIFTOOL_SET_CHANNEL_PARAM *inBuffer);
    bool setRebootDevice(string serviceAddr);
    bool setPTZ(string serviceAddr, ONVIFTOOL_SET_PTZ_PARAM *inBuffer);
    bool setPresets(string serviceAddr, ONVIFTOOL_PRESET_PARAM *inBuffer,
                    ONVIFTOOL_RET_PRESET_PARAM *outBuffer);

private:
    PDEVICE_FIND_CALLBACK deviceFindCB_;
    vector<string> manualAddr_;                                  // array 手动添加的设备的地址
    easyMap<string, vector<string>> handleUserMap_;              // map< handle, [username, password] >
    easyMap<string, LPONVIFTOOL_DEVICE_INFO> addrDeviceInfoMap_; // map< serviceAddr, deviceInfo >
    EasyBitMap<string, string> addrHandleMap_;                   // map< serviceAddr, handle>

    void *pUserData_;
    IOnvifTool *onvifTool_;
    int stopFlag;
    unsigned int interval_;
    unsigned int lastErr;

    onvifToolUtils utils;
};

onvifService::onvifService()
{
    stopFlag = 0;
    interval_ = 10;
    lastErr = 0;

    onvifTool_ = createOnvifTool();
    if (onvifTool_ == nullptr)
    {
        log("createOnvifTool err", LOG_CRITICAL);
        return;
    }

    onvifTool_->printLog([](string msg, int logLevel, void *userData)
                             -> void
                         {
                             auto utils = (onvifToolUtils *)userData;
                             utils->log(msg, logLevel); },
                         &utils);

    if (auto err = onvifTool_->init(); err)
    {
        log("onvifTool init err, delete onvifTool and exit", LOG_CRITICAL);
        deleteOnvifTool(onvifTool_);
        onvifTool_ = nullptr;
        return;
    }
    onvifTool_->setTimeOut(20);
}

onvifService::~onvifService()
{
    log("~onvifService", LOG_INFO);
    stopFlag = 1;
    deleteOnvifTool(onvifTool_);
    addrDeviceInfoMap_.clear();
}

unsigned int onvifService::getVersion()
{
    return 1;
}

bool onvifService::setLogToFile(int nLogLevel, char const *strLogDir, int bAutoDel)
{
    // 保存信息
    utils.setLogToFile(nLogLevel, strLogDir, bAutoDel);
    log("setLogToFile", LOG_DEBUG);
    return true;
}

bool onvifService::start(PDEVICE_FIND_CALLBACK pDeviceFindCallBack, int bInstallNPF, void *pUserData)
{
    if (onvifTool_ == nullptr)
    {
        log("createOnvifTool err, can not start", LOG_CRITICAL);
        return false;
    }

    stopFlag = 0;
    deviceFindCB_ = pDeviceFindCallBack;
    pUserData_ = pUserData;

    // 定时探测
    thread t_(&onvifService::autoInquery, this);
    t_.detach();

    // 开启监听
    thread t_hello(&onvifService::listenHello, this);
    t_hello.detach();
    thread t_bye(&onvifService::listenBye, this);
    t_bye.detach();

    return true;
}

bool onvifService::stop()
{
    stopFlag = 1;
    return true;
}

bool onvifService::login(const char *serviceAddr, const char *userName, const char *password,
                         char *handleOutBuffer, unsigned int outBufferSize)
{
    log("login device: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "login fail";

    if (handleOutBuffer == nullptr || outBufferSize < 64)
    {
        log(makeErrStr(errPrefix, "param error: handleOutBuffer at least has size 64"), LOG_ERROR);
        return false;
    }

    string jsonResponse;
    // 测试能否登录设备，不能登录就直接退出
    if (auto err = onvifTool_->getDeviceInformation(jsonResponse, serviceAddr, userName, password); err)
    {
        if (err->find("soap error: 28") < err->size())
        {
            // 连接失败，再次尝试
            log(makeErrStr(errPrefix + "serviceAddr: " + string(serviceAddr), *err), LOG_WARNING);
            log("try login again", LOG_WARNING);

            if (auto err_ = onvifTool_->getDeviceInformation(jsonResponse, serviceAddr, userName, password); err_)
            {
                log(makeErrStr(errPrefix + " serviceAddr: " + string(serviceAddr), *err), LOG_ERROR);
                return false;
            }
        }
        else
        {
            // 其他失败原因，直接退出
            log(makeErrStr(errPrefix + " serviceAddr: " + string(serviceAddr), *err), LOG_ERROR);
            return false;
        }
    }

    // 登录成功，判断是否是搜索到的设备，不是就主动添加设备
    if (auto [val, err] = addrDeviceInfoMap_.find(string(serviceAddr)); err != "")
    {
        log(string("login device: not found serviceAddr: ") + string(serviceAddr), LOG_WARNING);
        // 之前未搜索到该设备，主动添加该设备
        json deviceJson;
        deviceJson["serviceAddress"] = string(serviceAddr);
        if (addDevice(deviceJson, true) != 0)
        {
            log(string("login device: addDevice fail"), LOG_WARNING);
            return false;
        }

        // 添加到手动添加的数组里
        manualAddr_.push_back(string(serviceAddr));
        log(string("login device: add manually success, serviceAddr: ") + string(serviceAddr), LOG_INFO);
    }

    string handle;

    // 判断是否重复登录，重复登录就更新登录的用户名和密码，否则正常添加
    if (auto val = addrHandleMap_.findLeft(string(serviceAddr)); val)
    {
        // 之前登录过了, 更新用户名和密码
        handle = *val;
        handleUserMap_.erase(handle);
    }
    else
    {
        // 第一次登录

        // 产生唯一的handle
        handle = utils.generateUUID();

        // 绑定serverAddr与handle
        addrHandleMap_.insert(pair(string(serviceAddr), handle));
    }

    // 绑定用户名信息与handle
    vector<string> userInfo;
    userInfo.push_back(string(userName));
    userInfo.push_back(string(password));
    handleUserMap_.insert(pair(handle, userInfo));

    memset(handleOutBuffer, 0, outBufferSize);
    strcpy(handleOutBuffer, handle.c_str());
    return true;
}

bool onvifService::sendInquery()
{
    log("sendInquery", LOG_DEBUG);
    // 开始搜索设备
    string responseStr;
    if (auto err = onvifTool_->discoverDevice(responseStr); err)
    {
        log(makeErrStr("sendInquery fail", *err), LOG_ERROR);
        return false;
    }

    json j = json::parse(responseStr);
    auto count = j["count"].get<int>();
    auto devices = j["devices"].get<vector<json>>();

    // 将手动添加的每轮都主动更新一遍
    for (int i = 0; i < (int)manualAddr_.size(); i++)
    {
        json deviceJson;
        deviceJson["serviceAddress"] = manualAddr_[i];
        addDevice(deviceJson, true);
    }

    // 更新搜索到的
    for (int i = 0; i < count; i++)
    {
        addDevice(devices[i]);
    }
    printDeviceInfo();
    return true;
}

void onvifService::setAutoRequestInterval(unsigned int dwInterval)
{
    log(string("setAutoRequestInterval: ") + to_string(dwInterval), LOG_DEBUG);
    interval_ = dwInterval;
}

bool onvifService::modifyDeviceNetParam(const char *handle, const ONVIFTOOL_DEV_NET_PARAM *lpNetParam,
                                        ONVIFTOOL_DEV_RET_NET_PARAM *lpRetNetParam,
                                        unsigned int dwOutBuffSize)
{
    return true;
}

unsigned int onvifService::getLastError()
{
    return lastErr;
}

bool onvifService::clearup()
{
    return true;
}

bool onvifService::getDeviceConfig(const char *handle, unsigned int dwCommand,
                                   void *lpInBuffer, unsigned int dwInBuffSize,
                                   void *lpOutBuffer, unsigned int dwOutBuffSize)
{
    log(string("call getDeviceConfig, command: ") + to_string(dwCommand), LOG_INFO);
    // 获取服务地址
    string serviceAddr;
    auto val = addrHandleMap_.findRight(string(handle));
    if (val == nullopt)
    {
        log("handle is not found", LOG_ERROR);
        return false;
    }
    serviceAddr = *val;

    switch (dwCommand)
    {
    case ONVIFTOOL_GET_COMMAND_STREAM_URI:
    {
        if (dwInBuffSize != sizeof(ONVIFTOOL_GET_STREAM_PARAM) || lpInBuffer == nullptr)
        {
            log("lpInBuffer param error", LOG_ERROR);
            return false;
        }
        auto inBuffer = (LPONVIFTOOL_GET_STREAM_PARAM)lpInBuffer;
        if (dwOutBuffSize != sizeof(ONVIFTOOL_RET_GET_URI_PARAM) || lpOutBuffer == nullptr)
        {
            log("lpOutBuffer param error", LOG_ERROR);
            return false;
        }
        auto outBuffer = (LPONVIFTOOL_RET_GET_URI_PARAM)lpOutBuffer;

        return getStreamURI(serviceAddr, inBuffer, outBuffer);
    }
    break;
    case ONVIFTOOL_GET_COMMAND_DEVICE_INFO:
    {
        if (dwOutBuffSize != sizeof(ONVIFTOOL_RET_GET_DEVICE_INFO_PARAM))
        {
            log("lpOutBuffer param error", LOG_ERROR);
            return false;
        }
        auto outBuffer = (LPONVIFTOOL_RET_GET_DEVICE_INFO_PARAM)lpOutBuffer;

        return getDeviceInfo(serviceAddr, outBuffer);
    }
    break;
    case ONVIFTOOL_GET_COMMAND_SNAPSHOT_URI:
    {
        if (dwInBuffSize != sizeof(ONVIFTOOL_GET_SNAPSHOT_PARAM) || lpInBuffer == nullptr)
        {
            log("lpInBuffer param error", LOG_ERROR);
            return false;
        }
        auto inBuffer = (LPONVIFTOOL_GET_SNAPSHOT_PARAM)lpInBuffer;
        if (dwOutBuffSize != sizeof(ONVIFTOOL_RET_GET_URI_PARAM) || lpOutBuffer == nullptr)
        {
            log("lpOutBuffer param error", LOG_ERROR);
            return false;
        }
        auto outBuffer = (LPONVIFTOOL_RET_GET_URI_PARAM)lpOutBuffer;

        return getSnapShotURI(serviceAddr, inBuffer, outBuffer);
    }
    break;
    case ONVIFTOOL_GET_COMMAND_RECORD_LIST:
    {
        if (dwInBuffSize != sizeof(ONVIFTOOL_GET_RECORD_LIST_PARAM) || lpInBuffer == nullptr)
        {
            log("lpInBuffer param error", LOG_ERROR);
            return false;
        }
        auto inBuffer = (LPONVIFTOOL_GET_RECORD_LIST_PARAM)lpInBuffer;
        if (dwOutBuffSize != sizeof(ONVIFTOOL_RET_GET_RECORD_LIST_PARAM) || lpOutBuffer == nullptr)
        {
            log("lpOutBuffer param error", LOG_ERROR);
            return false;
        }
        auto outBuffer = (LPONVIFTOOL_RET_GET_RECORD_LIST_PARAM)lpOutBuffer;

        return getRecordList(serviceAddr, inBuffer, outBuffer);
    }
    break;
    case ONVIFTOOL_GET_COMMAND_RECORD_URI:
    {
        if (dwInBuffSize != sizeof(ONVIFTOOL_GET_RECORD_URI_PARAM) || lpInBuffer == nullptr)
        {
            log("lpInBuffer param error", LOG_ERROR);
            return false;
        }
        auto inBuffer = (LPONVIFTOOL_GET_RECORD_URI_PARAM)lpInBuffer;
        if (dwOutBuffSize != sizeof(ONVIFTOOL_RET_GET_URI_PARAM) || lpOutBuffer == nullptr)
        {
            log("lpOutBuffer param error", LOG_ERROR);
            return false;
        }
        auto outBuffer = (LPONVIFTOOL_RET_GET_URI_PARAM)lpOutBuffer;

        return getRecordURI(serviceAddr, inBuffer, outBuffer);
    }
    break;
    case ONVIFTOOL_GET_COMMAND_PRESETS:
    {
        if (dwInBuffSize != sizeof(ONVIFTOOL_PRESET_PARAM) || lpInBuffer == nullptr)
        {
            log("lpInBuffer param error", LOG_ERROR);
            return false;
        }
        auto inBuffer = (LPONVIFTOOL_PRESET_PARAM)lpInBuffer;
        if (dwOutBuffSize != sizeof(ONVIFTOOL_RET_PRESET_PARAM) || lpOutBuffer == nullptr)
        {
            log("lpOutBuffer param error", LOG_ERROR);
            return false;
        }
        auto outBuffer = (LPONVIFTOOL_RET_PRESET_PARAM)lpOutBuffer;

        return getPresets(serviceAddr, inBuffer, outBuffer);
    }
    break;

    default:
        break;
    }
    return false;
}

bool onvifService::setDeviceConfig(const char *handle, unsigned int dwCommand,
                                   void *lpInBuffer, unsigned int dwInBuffSize,
                                   void *lpOutBuffer, unsigned int dwOutBuffSize)
{
    log(string("call setDeviceConfig, command: ") + to_string(dwCommand), LOG_INFO);
    // 获取服务地址
    string serviceAddr;
    auto val = addrHandleMap_.findRight(string(handle));
    if (val == nullopt)
    {
        log("handle is not found", LOG_ERROR);
        return false;
    }
    serviceAddr = *val;

    switch (dwCommand)
    {
    case ONVIFTOOL_SET_COMMAND_CHANNEL_NAME:
    {
        if (dwInBuffSize != sizeof(ONVIFTOOL_SET_CHANNEL_PARAM) || lpInBuffer == nullptr)
        {
            log("lpInBuffer param error", LOG_ERROR);
            return false;
        }
        auto inBuffer = (LPONVIFTOOL_SET_CHANNEL_PARAM)lpInBuffer;

        return setChannelName(serviceAddr, inBuffer);
    }
    break;
    case ONVIFTOOL_SET_COMMAND_REBOOT:
    {
        return setRebootDevice(serviceAddr);
    }
    break;
    case ONVIFTOOL_SET_COMMAND_PTZ:
    {
        if (dwInBuffSize != sizeof(ONVIFTOOL_SET_PTZ_PARAM) || lpInBuffer == nullptr)
        {
            log("lpInBuffer param error", LOG_ERROR);
            return false;
        }
        auto inBuffer = (LPONVIFTOOL_SET_PTZ_PARAM)lpInBuffer;

        return setPTZ(serviceAddr, inBuffer);
    }
    break;
    case ONVIFTOOL_SET_COMMAND_PRESET:
    {
        if (dwInBuffSize != sizeof(ONVIFTOOL_PRESET_PARAM) || lpInBuffer == nullptr)
        {
            log("lpInBuffer param error", LOG_ERROR);
            return false;
        }
        auto inBuffer = (LPONVIFTOOL_PRESET_PARAM)lpInBuffer;
        if (dwOutBuffSize != sizeof(ONVIFTOOL_RET_PRESET_PARAM) || lpOutBuffer == nullptr)
        {
            log("lpOutBuffer param error", LOG_ERROR);
            return false;
        }
        auto outBuffer = (LPONVIFTOOL_RET_PRESET_PARAM)lpOutBuffer;

        return setPresets(serviceAddr, inBuffer, outBuffer);
    }
    break;

    default:
        break;
    }
    return false;
}

bool onvifService::resetPasswd(const char *handle,
                               const ONVIFTOOL_RESET_PARAM *pResetParam,
                               ONVIFTOOL_RET_RESET_PARAM *pRetResetParam)
{
    return true;
}

void onvifService::listenHelloDataCB(string jsonStr, void *pUserData)
{
    onvifService *instance = (onvifService *)pUserData;

    json device = json::parse(jsonStr);
    instance->addDevice(device);
}

void onvifService::listenByeDataCB(string jsonStr, void *pUserData)
{
    onvifService *instance = (onvifService *)pUserData;

    json device = json::parse(jsonStr);
    instance->deleteDevice(device);
}

void onvifService::log(string message, int logLevel)
{
    utils.log(message, logLevel);
}

void onvifService::listenHello()
{
    onvifTool_->listenHello(listenHelloDataCB, this);
}

void onvifService::listenBye()
{
    onvifTool_->listenBye(listenByeDataCB, this);
}

void onvifService::autoInquery()
{
    do
    {
        // 自动发送探测
        if (interval_ == 0)
        {
            std::this_thread::sleep_for(std::chrono::seconds(10));
            continue;
        }

        sendInquery();
        std::this_thread::sleep_for(std::chrono::seconds(interval_));
    } while (stopFlag != 1);
}

string onvifService::printDeviceInfo(ONVIFTOOL_DEVICE_INFO *pDeviceInfo_)
{
    if (pDeviceInfo_ == nullptr)
    {
        string deviceInfoStr = "";
        auto pDevInfo = addrDeviceInfoMap_.getAllValues();
        for (int i = 0; i < (int)pDevInfo.size(); i++)
        {
            char buffer[1000];
            sprintf(buffer, "\n第 %d 个设备", i + 1);
            deviceInfoStr += string(buffer);
            auto str = printDeviceInfo(pDevInfo[i]);
            deviceInfoStr += str;
        }
        log(deviceInfoStr, LOG_DEBUG);
        return deviceInfoStr;
    }

    string deviceInfoStr = "";
    deviceInfoStr += string("\n序列号: \t") + string(pDeviceInfo_->szSerialNO);
    deviceInfoStr += string("\n生产商: \t") + string(pDeviceInfo_->szManufacturer);
    deviceInfoStr += string("\n固件版本: \t") + string(pDeviceInfo_->szFirmwareVersion);
    deviceInfoStr += string("\n硬件id: \t") + string(pDeviceInfo_->szHardwareId);
    deviceInfoStr += string("\n型号: \t\t") + string(pDeviceInfo_->szModel);
    deviceInfoStr += string("\nonvif服务地址: \t") + string(pDeviceInfo_->szServiceAddr);
    deviceInfoStr += string("\n信息类型: \t") + to_string(pDeviceInfo_->iResult);
    deviceInfoStr += string("\n网卡个数: \t") + to_string(pDeviceInfo_->dwNetworkInterfaceSize);

    for (int i = 0; i < (int)pDeviceInfo_->dwNetworkInterfaceSize; i++)
    {
        deviceInfoStr += string("\n\t第 ") + to_string(i + 1) + string(" 个网卡信息");
        deviceInfoStr += string("\n\t网卡启用状态: \t") +
                         string((pDeviceInfo_->struNetworkInterfaceArray[i].byEnabled == 1 ? "启用" : "不启用"));
        deviceInfoStr += string("\n\t网卡名: \t") + string(pDeviceInfo_->struNetworkInterfaceArray[i].szName);
        deviceInfoStr += string("\n\t网卡MAC地址: \t") + string(pDeviceInfo_->struNetworkInterfaceArray[i].szMAC);
        deviceInfoStr += string("\n\t网卡MTU: \t") + to_string(pDeviceInfo_->struNetworkInterfaceArray[i].dwMTU);
        deviceInfoStr += string("\n\tIPv4地址启用状态: \t") +
                         string((pDeviceInfo_->struNetworkInterfaceArray[i].byIPv4Enabled == 1 ? "启用" : "不启用"));
        deviceInfoStr += string("\n\tIPv4地址: \t") + string(pDeviceInfo_->struNetworkInterfaceArray[i].szIPv4Address);
        deviceInfoStr += string("\n\tIPv4子网掩码: \t") + string(pDeviceInfo_->struNetworkInterfaceArray[i].szIPv4SubnetMask);
        deviceInfoStr += string("\n\tIPv4子网掩码前缀长度: \t") + to_string(pDeviceInfo_->struNetworkInterfaceArray[i].byIPv4MaskLen);
        deviceInfoStr += string("\n\tIPv4网关: \t") + string(pDeviceInfo_->struNetworkInterfaceArray[i].szIPv4Gateway);
        deviceInfoStr += string("\n\tIPv4 DHCP启用状态: \t") +
                         string((pDeviceInfo_->struNetworkInterfaceArray[i].byIPv4DHCPEnabled == 1 ? "启用" : "不启用"));
        deviceInfoStr += string("\n\tIPv6地址启用状态: \t") +
                         string((pDeviceInfo_->struNetworkInterfaceArray[i].byIPv6Enabled == 1 ? "启用" : "不启用"));
        deviceInfoStr += string("\n\tIPv6地址: \t") + string(pDeviceInfo_->struNetworkInterfaceArray[i].szIPv6Address);
        deviceInfoStr += string("\n\tIPv6子网掩码前缀长度: \t") + to_string(pDeviceInfo_->struNetworkInterfaceArray[i].byIPv6MaskLen);
        deviceInfoStr += string("\n\tIPv6网关: \t") + string(pDeviceInfo_->struNetworkInterfaceArray[i].szIPv6Gateway);
        deviceInfoStr += string("\n\tIPv6 DHCP启用状态: \t") +
                         string((pDeviceInfo_->struNetworkInterfaceArray[i].byIPv6DHCPEnabled == 1 ? "启用" : "不启用"));
    }

    deviceInfoStr += string("\n协议信息:");
    for (int i = 0; i < (int)pDeviceInfo_->dwProtocolInfoSize; i++)
    {
        deviceInfoStr += string("\n\t协议名: \t") + string(pDeviceInfo_->struProtocolInfoArray[i].szName);
        deviceInfoStr += string("\n\t协议是否启用: \t") +
                         string((pDeviceInfo_->struProtocolInfoArray[i].byEnabled == 1 ? "启用" : "不启用"));
        deviceInfoStr += string("\n\t协议端口号: \t") + to_string(pDeviceInfo_->struProtocolInfoArray[i].dwPort);
    }

    deviceInfoStr += string("\n通道信息:");
    for (int i = 0; i < pDeviceInfo_->struChannelInfo.byChannelSize; i++)
    {
        deviceInfoStr += string("\n\t通道号: \t") + to_string(i + 1);
        deviceInfoStr += string("\n\t通道名: \t") + string(pDeviceInfo_->struChannelInfo.szChannelNameArray[i]);
    }
    deviceInfoStr += string("\n\\**********************************************************\\\n\n");
    return deviceInfoStr;
}

string onvifService::makeErrStr(string errPrefix, string err)
{
    return errPrefix + ": " + err;
}

int onvifService::addDevice(const json device, bool manual)
{
    auto serviceAddr = device["serviceAddress"].get<string>();

    // 第一次添加标志
    bool firstAdd = false;
    if (auto [pDevInfo_, err] = addrDeviceInfoMap_.find(serviceAddr); err != "")
    {
        // 第一次添加
        ONVIFTOOL_DEVICE_INFO *pDevInfo = new ONVIFTOOL_DEVICE_INFO;
        memset(pDevInfo, 0, sizeof(ONVIFTOOL_DEVICE_INFO));
        pDevInfo->dwNetworkInterfaceSize = 0;
        pDevInfo->dwProtocolInfoSize = 0;
        memset(pDevInfo->struNetworkInterfaceArray, 0, 20 * sizeof(ONVIFTOOL_NETWORK_INTERFACE));
        memset(pDevInfo->struProtocolInfoArray, 0, 20 * sizeof(ONVIFTOOL_PROTOCOL_INFO));
        strcpy(pDevInfo->szServiceAddr, serviceAddr.c_str());
        pDevInfo->iResult = ONVIFTOOL_ADD;
        addrDeviceInfoMap_.insert(pair(serviceAddr, pDevInfo));
        firstAdd = true;
    }
    else if ((*pDevInfo_)->iResult == ONVIFTOOL_DEC)
    {
        if (!manual)
        {
            // 上一次下线了
            (*pDevInfo_)->iResult = ONVIFTOOL_RESTART;
        }
    }
    else
    {
        if (!manual)
        {
            // 上一次搜索过了
            (*pDevInfo_)->iResult = ONVIFTOOL_UPDATE;
        }
    }

    mutex devInfo, network, gateway, protocol, channel;
    bool devInfoFlag = true, networkFlag = true, gatewayFlag = true, protocolFlag = true, channelFlag = true;

    if (auto val = addrHandleMap_.findLeft(serviceAddr); val)
    {
        // 获取设备信息
        thread t1([this, &serviceAddr, &devInfo, &devInfoFlag]() -> void
                  {
                    devInfo.lock();
                    auto result = getDevInfomation(serviceAddr);
                    devInfoFlag = (result == 0?true:false);
                    devInfo.unlock(); 
                    ; });
        t1.detach();

        // 获取设备网络信息
        thread t2([this, &serviceAddr, &network, &networkFlag]() -> void
                  {
                    network.lock();
                    auto result = getNetworkInterface(serviceAddr);
                    networkFlag = (result == 0?true:false);
                    network.unlock(); 
                    ; });
        t2.detach();

        thread t3([this, &serviceAddr, &gateway, &gatewayFlag]() -> void
                  {
                    gateway.lock();
                    auto result = getNetworkDefaultGateway(serviceAddr);
                    gatewayFlag = (result == 0?true:false);
                    gateway.unlock(); 
                    ; });
        t3.detach();

        thread t4([this, &serviceAddr, &protocol, &protocolFlag]() -> void
                  {
                    protocol.lock();
                    auto result = getNetworkProtocols(serviceAddr);
                    protocolFlag = (result == 0?true:false);
                    protocol.unlock(); 
                    ; });
        t4.detach();

        // 获取通道信息
        thread t5([this, &serviceAddr, &channel, &channelFlag]() -> void
                  {
                    channel.lock();
                    auto result = getVideoChannel(serviceAddr);
                    channelFlag = (result == 0?true:false);
                    channel.unlock(); 
                    ; });
        t5.detach();
    }

    devInfo.lock();
    network.lock();
    gateway.lock();
    protocol.lock();
    channel.lock();
    auto [pDevInfo_, err] = addrDeviceInfoMap_.find(serviceAddr);

    if (manual && devInfoFlag && networkFlag && gatewayFlag && protocolFlag && channelFlag && !firstAdd)
    {
        // 手动添加的成功更新
        (*pDevInfo_)->iResult = ONVIFTOOL_UPDATE;
    }

    deviceFindCB_(*pDevInfo_, pUserData_);
    return 0;
}

int onvifService::deleteDevice(const json device)
{
    auto serviceAddr = device["serviceAddress"].get<string>();

    auto [pDevInfo_, err] = addrDeviceInfoMap_.find(serviceAddr);

    if (err == "")
    {
        (*pDevInfo_)->iResult = ONVIFTOOL_DEC;
        deviceFindCB_(*pDevInfo_, pUserData_);
    }

    return 0;
}

// 获取设备信息
int onvifService::getDevInfomation(string serviceAddr)
{
    log("call getDevInfomation: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getDevInfomation fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return -1;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return -1;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    auto [pDevInfo_, _] = addrDeviceInfoMap_.find(serviceAddr);

    // 发起请求
    string responseStr;
    if (auto err = onvifTool_->getDeviceInformation(responseStr, serviceAddr.c_str(), userName.c_str(), password.c_str()); err)
    {
        if ((*pDevInfo_)->iResult != ONVIFTOOL_DEC)
        {
            (*pDevInfo_)->iResult = ONVIFTOOL_UPDATEFAIL;
        }
        log(makeErrStr(errPrefix, *err), LOG_ERROR);
        return -1;
    }

    // 解析设备信息并保存
    json devInfoJson = json::parse(responseStr);
    strcpy((*pDevInfo_)->szSerialNO, devInfoJson["serialNumber"].get<string>().c_str());
    strcpy((*pDevInfo_)->szFirmwareVersion, devInfoJson["firmwareVersion"].get<string>().c_str());
    strcpy((*pDevInfo_)->szManufacturer, devInfoJson["manufacturer"].get<string>().c_str());
    strcpy((*pDevInfo_)->szHardwareId, devInfoJson["hardwareId"].get<string>().c_str());
    strcpy((*pDevInfo_)->szModel, devInfoJson["model"].get<string>().c_str());
    return 0;
}

// 获取指定服务的地址（PTZ、Media等）
int onvifService::getServices(string serviceAddr, string serviceNameFind, string &outAddr)
{
    // log("call getServices: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getServices fail";
    auto [pDevInfo_, error] = addrDeviceInfoMap_.find(serviceAddr);
    if (error != "")
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return -1;
    }
    // 发起请求
    string responseStr;
    if (auto err = onvifTool_->getServices(responseStr, serviceAddr.c_str()); err)
    {
        if ((*pDevInfo_)->iResult != ONVIFTOOL_DEC)
        {
            (*pDevInfo_)->iResult = ONVIFTOOL_UPDATEFAIL;
        }
        log(makeErrStr(errPrefix, *err) + ", serviceAddr: " + serviceAddr, LOG_ERROR);
        return -1;
    }

    // 解析设备服务
    json getServiceJson = json::parse(responseStr);
    auto serviceCount = getServiceJson["count"].get<int>();
    auto sevicesArray = getServiceJson["services"].get<vector<json>>();

    // 查找服务地址
    for (int j = 0; j < serviceCount; j++)
    {
        auto index = sevicesArray[j]["serviceAddress"].get<string>().find_last_of("/");
        auto serviceName = sevicesArray[j]["serviceAddress"].get<string>().substr(index + 1, -1);
        if (utils.compareServiceName(serviceNameFind, serviceName))
        {
            outAddr = sevicesArray[j]["serviceAddress"].get<string>();
            return 0;
        }
    }
    outAddr = "";
    log(string("getServices: serviceAddr: ") + serviceAddr + string(" not found service: ") + serviceNameFind, LOG_DEBUG);
    return 0;
}

// 获取网卡信息
int onvifService::getNetworkInterface(string serviceAddr)
{
    log("call getNetworkInterface: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getNetworkInterface fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return -1;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return -1;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    auto [pDevInfo_, _] = addrDeviceInfoMap_.find(serviceAddr);

    // 发起请求
    string responseStr;
    if (auto err = onvifTool_->getNetworkInterfaces(responseStr, serviceAddr.c_str(), userName.c_str(), password.c_str()); err)
    {
        if ((*pDevInfo_)->iResult != ONVIFTOOL_DEC)
        {
            (*pDevInfo_)->iResult = ONVIFTOOL_UPDATEFAIL;
        }
        log(makeErrStr(errPrefix, *err), LOG_ERROR);
        return -1;
    }

    // 解析返回信息
    json j = json::parse(responseStr);
    auto interfaces = j["interfaces"].get<vector<json>>();
    (*pDevInfo_)->dwNetworkInterfaceSize = interfaces.size() > 20 ? 20 : interfaces.size();

    // 清除上一次的信息
    memset((*pDevInfo_)->struNetworkInterfaceArray, 0, 20 * sizeof(ONVIFTOOL_NETWORK_INTERFACE));

    // 保存网卡信息
    for (int i = 0; i < (int)(*pDevInfo_)->dwNetworkInterfaceSize; i++)
    {

        (*pDevInfo_)->struNetworkInterfaceArray[i].byEnabled = interfaces[i]["enabled"].get<string>() == "true" ? 1 : 0;

        if (interfaces[i].find("name") != interfaces[i].end())
        {
            strcpy((*pDevInfo_)->struNetworkInterfaceArray[i].szName, interfaces[i]["name"].get<string>().c_str());
        }
        if (interfaces[i].find("mac") != interfaces[i].end())
        {
            strcpy((*pDevInfo_)->struNetworkInterfaceArray[i].szMAC, interfaces[i]["mac"].get<string>().c_str());
        }
        if (interfaces[i].find("MTU") != interfaces[i].end())
        {
            string MTU = interfaces[i]["MTU"].get<string>();
            (*pDevInfo_)->struNetworkInterfaceArray[i].dwMTU = MTU != "" ? stoi(MTU) : -1;
        }

        if (interfaces[i].find("IPv4") != interfaces[i].end())
        {
            (*pDevInfo_)->struNetworkInterfaceArray[i].byIPv4Enabled = interfaces[i]["IPv4"]["enabled"].get<string>() == "true" ? 1 : 0;

            if (interfaces[i]["IPv4"].find("config") != interfaces[i]["IPv4"].end())
            {
                bool useDHCP = interfaces[i]["IPv4"]["config"]["useDHCP"].get<string>() == "true" ? true : false;
                (*pDevInfo_)->struNetworkInterfaceArray[i].byIPv4DHCPEnabled = useDHCP ? 1 : 0;

                if (useDHCP)
                {
                    auto fromDHCP = interfaces[i]["IPv4"]["config"]["fromDHCP"].get<vector<json>>();
                    if (fromDHCP.size() > 0)
                    {
                        strcpy((*pDevInfo_)->struNetworkInterfaceArray[i].szIPv4Address, fromDHCP[0]["address"].get<string>().c_str());
                        (*pDevInfo_)->struNetworkInterfaceArray[i].byIPv4MaskLen = stoi(fromDHCP[0]["prefixLength"].get<string>());
                        strcpy((*pDevInfo_)->struNetworkInterfaceArray[i].szIPv4SubnetMask, utils.prefixToSubmask((int)(*pDevInfo_)->struNetworkInterfaceArray[i].byIPv4MaskLen).c_str());
                    }
                }
                else
                {
                    auto manual = interfaces[i]["IPv4"]["config"]["manual"].get<vector<json>>();
                    if (manual.size() > 0)
                    {
                        strcpy((*pDevInfo_)->struNetworkInterfaceArray[i].szIPv4Address, manual[0]["address"].get<string>().c_str());
                        (*pDevInfo_)->struNetworkInterfaceArray[i].byIPv4MaskLen = stoi(manual[0]["prefixLength"].get<string>());
                        strcpy((*pDevInfo_)->struNetworkInterfaceArray[i].szIPv4SubnetMask, utils.prefixToSubmask((int)(*pDevInfo_)->struNetworkInterfaceArray[i].byIPv4MaskLen).c_str());
                    }
                }
            }
        }

        if (interfaces[i].find("IPv6") != interfaces[i].end())
        {
            (*pDevInfo_)->struNetworkInterfaceArray[i].byIPv6Enabled = interfaces[i]["IPv6"]["enabled"].get<string>() == "true" ? 1 : 0;

            if (interfaces[i]["IPv6"].find("config") != interfaces[i]["IPv6"].end())
            {
                bool useDHCP = interfaces[i]["IPv6"]["config"]["DHCP"].get<string>() == "off" ? false : true;
                (*pDevInfo_)->struNetworkInterfaceArray[i].byIPv6DHCPEnabled = useDHCP ? 1 : 0;

                if (useDHCP)
                {
                    auto fromDHCP = interfaces[i]["IPv6"]["config"]["fromDHCP"].get<vector<json>>();
                    if (fromDHCP.size() > 0)
                    {
                        strcpy((*pDevInfo_)->struNetworkInterfaceArray[i].szIPv6Address, fromDHCP[0]["address"].get<string>().c_str());
                        (*pDevInfo_)->struNetworkInterfaceArray[i].byIPv6MaskLen = stoi(fromDHCP[0]["prefixLength"].get<string>());
                    }
                }
                else
                {
                    auto manual = interfaces[i]["IPv6"]["config"]["manual"].get<vector<json>>();
                    if (manual.size() > 0)
                    {
                        strcpy((*pDevInfo_)->struNetworkInterfaceArray[i].szIPv6Address, manual[0]["address"].get<string>().c_str());
                        (*pDevInfo_)->struNetworkInterfaceArray[i].byIPv6MaskLen = stoi(manual[0]["prefixLength"].get<string>());
                    }
                }
            }
        }
    }
    return 0;
}

// 获取默认网关
int onvifService::getNetworkDefaultGateway(string serviceAddr)
{
    log("call getNetworkDefaultGateway: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getNetworkDefaultGateway fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return -1;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return -1;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    auto [pDevInfo_, _] = addrDeviceInfoMap_.find(serviceAddr);

    // 发起请求
    string responseStr;
    if (auto err = onvifTool_->getNetworkDefaultGateway(responseStr, serviceAddr.c_str(), userName.c_str(), password.c_str()); err)
    {
        if ((*pDevInfo_)->iResult != ONVIFTOOL_DEC)
        {
            (*pDevInfo_)->iResult = ONVIFTOOL_UPDATEFAIL;
        }
        log(makeErrStr(errPrefix, *err), LOG_ERROR);
        return -1;
    }

    // 解析返回信息
    json j = json::parse(responseStr);

    auto IPv4Address = j["IPv4Address"].get<vector<string>>();
    auto IPv6Address = j["IPv6Address"].get<vector<string>>();

    for (int i = 0; i < (int)(*pDevInfo_)->dwNetworkInterfaceSize; i++)
    {
        if (IPv4Address.size() > 0)
        {
            strcpy((*pDevInfo_)->struNetworkInterfaceArray[i].szIPv4Gateway, IPv4Address[0].c_str());
        }
        if (IPv6Address.size() > 0)
        {
            strcpy((*pDevInfo_)->struNetworkInterfaceArray[i].szIPv6Gateway, IPv6Address[0].c_str());
        }
    }
    return 0;
}

// 获取网络协议
int onvifService::getNetworkProtocols(string serviceAddr)
{
    log("call getNetworkProtocols: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getNetworkProtocols fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return -1;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return -1;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    auto [pDevInfo_, _] = addrDeviceInfoMap_.find(serviceAddr);

    // 发起请求
    string responseStr;
    if (auto err = onvifTool_->getNetworkProtocols(responseStr, serviceAddr.c_str(), userName.c_str(), password.c_str()); err)
    {
        if ((*pDevInfo_)->iResult != ONVIFTOOL_DEC)
        {
            (*pDevInfo_)->iResult = ONVIFTOOL_UPDATEFAIL;
        }
        log(makeErrStr(errPrefix, *err), LOG_ERROR);
        return -1;
    }

    // 解析返回信息
    json j = json::parse(responseStr);
    auto protocols = j["protocols"].get<vector<json>>();
    (*pDevInfo_)->dwProtocolInfoSize = protocols.size() > 20 ? 20 : protocols.size();

    // 清除上一次的信息
    memset((*pDevInfo_)->struProtocolInfoArray, 0, sizeof(ONVIFTOOL_PROTOCOL_INFO) * 20);

    // 保存协议信息
    for (int i = 0; i < (int)(*pDevInfo_)->dwProtocolInfoSize; i++)
    {
        strcpy((*pDevInfo_)->struProtocolInfoArray[i].szName, protocols[i]["name"].get<string>().c_str());
        (*pDevInfo_)->struProtocolInfoArray[i].byEnabled = protocols[i]["enabled"].get<string>() == "true" ? 1 : 0;
        auto ports = protocols[i]["ports"].get<vector<int>>();
        if (ports.size() > 0)
        {
            (*pDevInfo_)->struProtocolInfoArray[i].dwPort = ports[0];
        }
    }
    return 0;
}

// 通过通道名，获取通道对应视频源配置令牌
string onvifService::getVideoSourceConfigToken(string serviceAddr, int channel)
{
    log("call getVideoSourceConfigToken: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getVideoSourceConfigToken fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return "";
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return "";
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 获取服务地址
    string media2Service = "";
    string mediaService = "";
    // 获取媒体服务地址
    getServices(serviceAddr, "media2_service", media2Service);
    getServices(serviceAddr, "media_service", mediaService);

    // 发起请求获取通道对应的视频源
    string responseJson = "";
    if (media2Service != "")
    {
        log("media2_service", LOG_DEBUG);
        if (auto err = onvifTool_->getOSDs_V2(responseJson, media2Service.c_str(), userName.c_str(), password.c_str()); err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return "";
        }
    }
    else if (mediaService != "")
    {
        log("media_service", LOG_DEBUG);
        if (auto err = onvifTool_->getOSDs_V1(responseJson, mediaService.c_str(), userName.c_str(), password.c_str()); err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return "";
        }
    }
    else
    {
        log(makeErrStr(errPrefix, string("device: ") + serviceAddr + " not suppose media_service and media2_service"), LOG_ERROR);
        return "";
    }

    // 解析返回内容获取channel对应的视频源
    string videoSourceConfigurationToken = "";
    json j = json::parse(responseJson);
    auto OSDs = j["OSDs"].get<vector<json>>();
    unordered_map<string, string> sourceChannelMap; // map<source, channelName>
    vector<string> channelNameVec;                  // 按顺序保存的channelName
    for (int i = 0; i < (int)OSDs.size(); i++)
    {

        if (OSDs[i].find("videoSourceConfigurationToken") == OSDs[i].end() || OSDs[i]["videoSourceConfigurationToken"] == "")
        {
            continue;
        }
        if (OSDs[i].find("type") == OSDs[i].end() || OSDs[i]["type"] != "text")
        {
            continue;
        }
        if (OSDs[i].find("textString") == OSDs[i].end() ||
            OSDs[i]["textString"].find("type") == OSDs[i]["textString"].end() ||
            OSDs[i]["textString"]["type"] != "Plain")
        {
            continue;
        }
        if (OSDs[i]["textString"].find("plainText") == OSDs[i]["textString"].end() ||
            OSDs[i]["textString"]["plainText"] == "")
        {
            continue;
        }
        videoSourceConfigurationToken = OSDs[i]["videoSourceConfigurationToken"].get<string>();
        auto channelName = OSDs[i]["textString"]["plainText"].get<string>();

        // 视频源第一个文本OSD为通道名
        if (sourceChannelMap.find(videoSourceConfigurationToken) == sourceChannelMap.end())
        {
            sourceChannelMap[videoSourceConfigurationToken] = channelName;

            // 按顺序保存通道名
            channelNameVec.push_back(channelName);
            // 当前通道号是选择的通道号
            if ((int)channelNameVec.size() == channel)
            {
                break;
            }
        }
        videoSourceConfigurationToken = "";
    }

    return videoSourceConfigurationToken;
}

// 获取视频源配置令牌对应的视频源令牌
string onvifService::getVideoSourceToken(string serviceAddr, string videoSourceConfigToken)
{
    log("call getVideoSourceToken: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getVideoSourceToken fail";
    string videoSourceToken = "";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return videoSourceToken;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return videoSourceToken;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 获取服务地址
    string media2Service = "";
    string mediaService = "";
    // 获取媒体服务地址
    getServices(serviceAddr, "media2_service", media2Service);
    getServices(serviceAddr, "media_service", mediaService);

    string responseJson;
    if (media2Service != "")
    {
        // 获取videoSource
        if (auto err = onvifTool_->getVideoSourceConfigurations_V2(responseJson, media2Service.c_str(), userName.c_str(),
                                                                   password.c_str(), videoSourceConfigToken.c_str(), nullptr);
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return videoSourceToken;
        }
        json j = json::parse(responseJson);
        auto configurationsVec = j["configurations"].get<vector<json>>();
        for (int i = 0; i < (int)configurationsVec.size(); i++)
        {
            if (configurationsVec[i]["token"] == videoSourceConfigToken)
            {
                videoSourceToken = configurationsVec[i]["sourceToken"].get<string>();
                break;
            }
        }
    }
    else if (mediaService != "")
    {
        // 获取videoSource
        if (auto err = onvifTool_->getVideoSourceConfiguration_V1(responseJson, mediaService.c_str(), userName.c_str(),
                                                                  password.c_str(), videoSourceConfigToken.c_str());
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return videoSourceToken;
        }
        json j = json::parse(responseJson);
        videoSourceToken = j["sourceToken"].get<string>();
    }

    return videoSourceToken;
}

// 通过视频源配置令牌获取对应的媒体配置令牌列表
vector<string> onvifService::getProfileTokenList(string serviceAddr, string VideoSourceConfigToken)
{
    log("call getProfileTokenList: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getProfileTokenList fail";
    vector<string> profileToken;
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return profileToken;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return profileToken;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 获取服务地址
    string media2Service = "";
    string mediaService = "";
    // 获取媒体服务地址
    getServices(serviceAddr, "media2_service", media2Service);
    getServices(serviceAddr, "media_service", mediaService);

    string responseJson;
    if (media2Service != "")
    {
        // 获取videoSource对应的profile
        ONVIF_CONFIGURATION_ENUMERATION type;
        unsigned int typeArray[1] = {0};
        type.size = 1;
        type.tokenArray = nullptr;
        type.typeArray = typeArray;
        if (auto err = onvifTool_->getProfiles_V2(responseJson, media2Service.c_str(), userName.c_str(),
                                                  password.c_str(), nullptr, &type);
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return profileToken;
        }
        json j = json::parse(responseJson);
        auto profilesVec = j["profiles"].get<vector<json>>();
        for (int i = 0; i < (int)profilesVec.size(); i++)
        {
            if (profilesVec[i]["videoSource"]["token"] == VideoSourceConfigToken)
            {
                profileToken.push_back(profilesVec[i]["token"].get<string>());
            }
        }
    }
    else if (mediaService != "")
    {
        // 获取videoSource对应的profile
        if (auto err = onvifTool_->getProfiles_V1(responseJson, mediaService.c_str(), userName.c_str(), password.c_str()); err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return profileToken;
        }
        json j = json::parse(responseJson);
        auto profilesVec = j["profiles"].get<vector<json>>();
        for (int i = 0; i < (int)profilesVec.size(); i++)
        {
            if (profilesVec[i]["videoSourceConfiguration"]["token"] == VideoSourceConfigToken)
            {
                profileToken.push_back(profilesVec[i]["token"].get<string>());
            }
        }
    }

    return profileToken;
}

// 获取视频通道信息
int onvifService::getVideoChannel(string serviceAddr)
{
    log("call getVideoChannel: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getVideoChannel fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return -1;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return -1;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    auto [pDevInfo_, _] = addrDeviceInfoMap_.find(serviceAddr);

    // 获取服务地址
    string media2Service = "";
    string mediaService = "";
    // 获取媒体服务地址
    getServices(serviceAddr, "media2_service", media2Service);
    getServices(serviceAddr, "media_service", mediaService);

    // 发起请求
    string responseJson = "";
    if (media2Service != "")
    {
        log("media2_service", LOG_DEBUG);
        if (auto err = onvifTool_->getOSDs_V2(responseJson, media2Service.c_str(), userName.c_str(), password.c_str()); err)
        {
            if ((*pDevInfo_)->iResult != ONVIFTOOL_DEC)
            {
                (*pDevInfo_)->iResult = ONVIFTOOL_UPDATEFAIL;
            }
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return -1;
        }
    }
    else if (mediaService != "")
    {
        log("media_service", LOG_DEBUG);
        if (auto err = onvifTool_->getOSDs_V1(responseJson, mediaService.c_str(), userName.c_str(), password.c_str()); err)
        {
            if ((*pDevInfo_)->iResult != ONVIFTOOL_DEC)
            {
                (*pDevInfo_)->iResult = ONVIFTOOL_UPDATEFAIL;
            }
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return -1;
        }
    }
    else
    {
        log(makeErrStr(errPrefix, string("device: ") + serviceAddr + " not suppose media_service and media2_service"), LOG_ERROR);
        return -1;
    }

    if (responseJson == "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return -1;
    }

    // 解析返回内容
    json j = json::parse(responseJson);
    auto OSDs = j["OSDs"].get<vector<json>>();
    unordered_map<string, string> sourceChannelMap; // map <source, channelName>;
    vector<string> channelNameVec;                  // 按顺序保存通道名
    for (int i = 0; i < (int)OSDs.size(); i++)
    {

        if (OSDs[i].find("videoSourceConfigurationToken") == OSDs[i].end() || OSDs[i]["videoSourceConfigurationToken"] == "")
        {
            continue;
        }
        if (OSDs[i].find("type") == OSDs[i].end() || OSDs[i]["type"] != "text")
        {
            continue;
        }
        if (OSDs[i].find("textString") == OSDs[i].end() ||
            OSDs[i]["textString"].find("type") == OSDs[i]["textString"].end() ||
            OSDs[i]["textString"]["type"] != "Plain")
        {
            continue;
        }
        if (OSDs[i]["textString"].find("plainText") == OSDs[i]["textString"].end() ||
            OSDs[i]["textString"]["plainText"] == "")
        {
            continue;
        }
        auto videoSourceToken = OSDs[i]["videoSourceConfigurationToken"].get<string>();
        auto channelName = OSDs[i]["textString"]["plainText"].get<string>();

        // 视频源的第一个文本OSD是通道名
        if (sourceChannelMap.find(videoSourceToken) == sourceChannelMap.end())
        {
            sourceChannelMap[videoSourceToken] = channelName;
            // 按顺序保存通道名
            channelNameVec.push_back(channelName);
        }
    }

    // 更新通道信息(最多只保存50个通道信息)
    (*pDevInfo_)->struChannelInfo.byChannelSize = channelNameVec.size() > 50 ? 50 : channelNameVec.size();
    memset((*pDevInfo_)->struChannelInfo.szChannelNameArray, 0, 50 * 300);
    for (int i = 0; i < (*pDevInfo_)->struChannelInfo.byChannelSize; i++)
    {
        strcpy((*pDevInfo_)->struChannelInfo.szChannelNameArray[i], channelNameVec[i].c_str());
    }

    return 0;
}

// 通过命令码获取流URI
bool onvifService::getStreamURI(string serviceAddr, ONVIFTOOL_GET_STREAM_PARAM *inBuffer,
                                ONVIFTOOL_RET_GET_URI_PARAM *outBuffer)
{
    log("call getStreamURI: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getStreamURI fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return false;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return false;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 获取服务地址
    string media2Service = "";
    string mediaService = "";
    // 获取媒体服务地址
    getServices(serviceAddr, "media2_service", media2Service);
    getServices(serviceAddr, "media_service", mediaService);

    // 解析返回内容获取channel对应的视频源
    string responseJson = "";
    string videoSourceConfigurationToken = "";
    videoSourceConfigurationToken = getVideoSourceConfigToken(serviceAddr, (int)inBuffer->dwChannelNum);

    if (videoSourceConfigurationToken == "")
    {
        string errStr = serviceAddr + string(", channelNum: ") +
                        to_string((int)inBuffer->dwChannelNum) +
                        string(", has no video source");
        log(errStr, LOG_ERROR);
        return false;
    }

    vector<string> profileToken = getProfileTokenList(serviceAddr, videoSourceConfigurationToken);
    // 码流类型
    int streamType = (int)inBuffer->byStreamType;
    if ((int)profileToken.size() < streamType + 1)
    {
        string errStr = serviceAddr + string(", channelNum: ") +
                        to_string((int)inBuffer->dwChannelNum) +
                        string(", no stream found");
        log(errStr, LOG_ERROR);
        return false;
    }
    json j;

    if (media2Service != "")
    {

        // 获取streamURI
        ONVIF_TRANSPORT_PROTOCOL_V2 protocol;
        protocol.protocolsName = (unsigned int)inBuffer->byTransportType;
        if (auto err = onvifTool_->getStreamUri_V2(responseJson, media2Service.c_str(), userName.c_str(),
                                                   password.c_str(), profileToken[streamType].c_str(), protocol);
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
    }
    else if (mediaService != "")
    {
        // 获取streamURI
        ONVIF_STREAM_SETUP_PARAM streamSetup;
        streamSetup.transport.tunnel = nullptr;
        switch (inBuffer->byTransportType)
        {
        case 0:
            streamSetup.streamType = 0;
            streamSetup.transport.protocolsName = 0;
            break;
        case 1:
            streamSetup.streamType = 1;
            streamSetup.transport.protocolsName = 0;
            break;
        case 2:
            streamSetup.streamType = 0;
            streamSetup.transport.protocolsName = 2;
            break;
        case 3:
            streamSetup.streamType = 0;
            streamSetup.transport.protocolsName = 0;
            break;
        case 4:
            streamSetup.streamType = 1;
            streamSetup.transport.protocolsName = 0;
            break;
        case 5:
            streamSetup.streamType = 0;
            streamSetup.transport.protocolsName = 3;
            break;

        default:
            break;
        }
        if (auto err = onvifTool_->getStreamUri_V1(responseJson, mediaService.c_str(), userName.c_str(),
                                                   password.c_str(), profileToken[streamType].c_str(), &streamSetup);
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
    }

    // 将URI写入outBuffer
    j = json::parse(responseJson);
    auto URI = j["uri"].get<string>();
    string newURI = "";

    // 为URI附带上用户名和密码
    if (URI.substr(0, 4) == "rtsp")
    {
        newURI = URI.substr(0, 7) + userName + string(":") + password + string("@") + URI.substr(7);
    }
    else
    {
        newURI = URI;
    }
    memset(outBuffer->URI, 0, sizeof(outBuffer->URI));
    strcpy(outBuffer->URI, newURI.c_str());
    return true;
}

// 通过命令码获取设备信息
bool onvifService::getDeviceInfo(string serviceAddr, ONVIFTOOL_RET_GET_DEVICE_INFO_PARAM *outBuffer)
{
    log("call getDeviceInfo: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getDeviceInfo fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return false;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return false;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 不自动更新的，更新设备信息
    if (interval_ == 0)
    {
        json deviceJson;
        deviceJson["serviceAddress"] = serviceAddr;
        addDevice(deviceJson);
    }

    // 将更新后的信息传出
    auto [pDevInfo_, _] = addrDeviceInfoMap_.find(serviceAddr);
    memset(outBuffer, 0, (size_t)sizeof(outBuffer));
    strcpy(outBuffer->szSerialNO, (*pDevInfo_)->szSerialNO);
    strcpy(outBuffer->szManufacturer, (*pDevInfo_)->szManufacturer);
    strcpy(outBuffer->szFirmwareVersion, (*pDevInfo_)->szFirmwareVersion);
    strcpy(outBuffer->szHardwareId, (*pDevInfo_)->szHardwareId);
    strcpy(outBuffer->szModel, (*pDevInfo_)->szModel);
    strcpy(outBuffer->szMAC, (*pDevInfo_)->struNetworkInterfaceArray[0].szMAC);

    switch ((*pDevInfo_)->iResult)
    {
    case ONVIFTOOL_DEC:
        outBuffer->iOnline = 0;
        break;
    case ONVIFTOOL_UPDATEFAIL:
        outBuffer->iOnline = 0;
        break;

    default:
        outBuffer->iOnline = 1;
        break;
    }

    outBuffer->iChannelSize = (int)(*pDevInfo_)->struChannelInfo.byChannelSize;
    for (int i = 0; i < outBuffer->iChannelSize; i++)
    {
        strcpy(outBuffer->szChannelNameArray[i], (*pDevInfo_)->struChannelInfo.szChannelNameArray[i]);
        // 判断通道是否在线
        ONVIFTOOL_GET_STREAM_PARAM getStreamParam = {0};
        getStreamParam.dwChannelNum = i + 1;
        getStreamParam.byStreamType = 0;
        getStreamParam.byTransportType = 0;
        ONVIFTOOL_RET_GET_URI_PARAM getStreamResult = {0};

        getStreamURI(serviceAddr, &getStreamParam, &getStreamResult);

        if (string(getStreamResult.URI) != "")
        {
            outBuffer->iChannelOnline[i] = 1;
        }
        else
        {
            outBuffer->iChannelOnline[i] = 0;
        }
    }

    return true;
}

// 通过命令码获取抓图URI
bool onvifService::getSnapShotURI(string serviceAddr, ONVIFTOOL_GET_SNAPSHOT_PARAM *inBuffer,
                                  ONVIFTOOL_RET_GET_URI_PARAM *outBuffer)
{
    log("call getSnapShotURI: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getSnapShotURI fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return false;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return false;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 获取服务地址
    string media2Service = "";
    string mediaService = "";
    // 获取媒体服务地址
    getServices(serviceAddr, "media2_service", media2Service);
    getServices(serviceAddr, "media_service", mediaService);

    // 发起请求获取通道对应的视频源
    string videoSourceConfigurationToken = "";
    videoSourceConfigurationToken = getVideoSourceConfigToken(serviceAddr, (int)inBuffer->dwChannelNum);

    if (videoSourceConfigurationToken == "")
    {
        string errStr = serviceAddr + string(", channelNum: ") +
                        to_string((int)inBuffer->dwChannelNum) +
                        string(", has no video source");
        log(errStr, LOG_ERROR);
        return false;
    }

    vector<string> profileToken = getProfileTokenList(serviceAddr, videoSourceConfigurationToken);
    if (profileToken.size() == 0)
    {
        string errStr = serviceAddr + string(", channelNum: ") +
                        to_string((int)inBuffer->dwChannelNum) +
                        string(", no stream found");
        log(errStr, LOG_ERROR);
        return false;
    }
    string responseJson;

    if (media2Service != "")
    {
        // 获取SnapShotURI
        if (auto err = onvifTool_->getSnapshotUri_V2(responseJson, media2Service.c_str(), userName.c_str(),
                                                     password.c_str(), profileToken[0].c_str());
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
    }
    else if (mediaService != "")
    {
        // 获取SnapshotURI
        if (auto err = onvifTool_->getSnapshotUri_V1(responseJson, mediaService.c_str(), userName.c_str(),
                                                     password.c_str(), profileToken[0].c_str());
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
    }

    // 将URI写入outBuffer
    json j = json::parse(responseJson);
    auto URI = j["uri"].get<string>();

    memset(outBuffer->URI, 0, sizeof(outBuffer->URI));
    strcpy(outBuffer->URI, URI.c_str());
    return true;
}

// 通过命令码获取回放列表
bool onvifService::getRecordList(string serviceAddr, ONVIFTOOL_GET_RECORD_LIST_PARAM *inBuffer,
                                 ONVIFTOOL_RET_GET_RECORD_LIST_PARAM *outBuffer)
{
    log("call getRecordList: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getRecordList fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return false;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return false;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 获取服务地址
    string searchService = "";
    // 获取媒体服务地址
    getServices(serviceAddr, "search_service", searchService);

    string responseJson = "";
    if (searchService == "")
    {
        log(makeErrStr(errPrefix, string("device: ") + serviceAddr + " not suppose search_service"), LOG_ERROR);
        return false;
    }

    memset(outBuffer, 0, sizeof(ONVIFTOOL_RET_GET_RECORD_LIST_PARAM));
    time_t requestBeginTime = utils.localTimeToTime_t(string(inBuffer->szBeginTime));
    time_t requestEndTime = utils.localTimeToTime_t(string(inBuffer->szEndTime));
    json j;

    // 查询范围内是否存在回放记录
    if (auto err = onvifTool_->getRecordingSummary(responseJson, searchService.c_str(), userName.c_str(), password.c_str()); err)
    {
        log(makeErrStr(errPrefix, *err), LOG_WARNING);
    }
    else
    {
        // 解析返回内容，判断范围内是否有回放记录
        j = json::parse(responseJson);
        time_t responseBeginTime = utils.dateTimeToTime_t(j["dataFrom"].get<string>());
        time_t responseEndTime = utils.dateTimeToTime_t(j["dataUntil"].get<string>());

        // 请求范围内没有记录
        if (j["numberRecordings"] == 0 || requestBeginTime > responseEndTime || requestEndTime < responseBeginTime)
        {
            outBuffer->byListSize = 0;
            outBuffer->byMore = 0;
            log("getRecordList: getRecordingSummary: no records in request's range", LOG_DEBUG);
            return true;
        }
    }

    // 发起请求获取通道对应的视频源
    string videoSourceConfigurationToken = "";
    videoSourceConfigurationToken = getVideoSourceConfigToken(serviceAddr, (int)inBuffer->dwChannelNum);

    if (videoSourceConfigurationToken == "")
    {
        string errStr = serviceAddr + string(", channelNum: ") +
                        to_string((int)inBuffer->dwChannelNum) +
                        string(", has no video source");
        log(errStr, LOG_ERROR);
        return false;
    }

    // 解析返回内容获取channel对应的视频源
    vector<string> profileToken = getProfileTokenList(serviceAddr, videoSourceConfigurationToken);
    if (profileToken.size() == 0)
    {
        string errStr = serviceAddr + string(", channelNum: ") +
                        to_string((int)inBuffer->dwChannelNum) +
                        string(", no stream found");
        log(errStr, LOG_ERROR);
        return false;
    }

    // 开始搜索媒体源对应的记录
    ONVIF_FIND_RECORDINGS_PARAM param;
    param.includeRecordingsToken = nullptr;
    param.includeRecordingsTokenSize = 0;

    int profileType = 0;
    int **includeSourcesType = new int *[profileToken.size()];
    char **includeSourcesToken = new char *[profileToken.size()];
    for (int i = 0; i < (int)profileToken.size(); i++)
    {
        includeSourcesType[i] = &profileType;
        includeSourcesToken[i] = (char *)profileToken[i].c_str();
    }
    param.includeSourcesToken = includeSourcesToken;
    param.includeSourcesType = includeSourcesType;
    param.includeSourcesTokenSize = profileToken.size();
    param.keepAliveTime = (char *)"PT60S";
    param.maxMatches = nullptr;
    param.recordingInformationFilter = (char *)"boolean(//Track[TrackType = \"Video\"])";
    if (auto err = onvifTool_->findRecordings(responseJson, searchService.c_str(), userName.c_str(), password.c_str(), &param))
    {
        log(makeErrStr(errPrefix, *err), LOG_ERROR);
        delete[] includeSourcesType;
        delete[] includeSourcesToken;
        return false;
    }
    delete[] includeSourcesType;
    delete[] includeSourcesToken;

    j = json::parse(responseJson);
    string searchToken = j["searchToken"].get<string>();

    vector<json> recordingList;

    // 获取搜索结果
    do
    {
        // 获取搜索结果
        this_thread::sleep_for(chrono::seconds(5));
        if (auto err = onvifTool_->getRecordingSearchResults(responseJson, searchService.c_str(),
                                                             userName.c_str(), password.c_str(), searchToken.c_str());
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
        j = json::parse(responseJson);
        auto recordingInformation = j["recordingInformation"].get<vector<json>>();
        for (int i = 0; i < (int)recordingInformation.size(); i++)
        {
            recordingList.push_back(recordingInformation[i]);
        }

        if (j["searchState"] == "completed" || j["searchState"] == "unknown")
        {
            break;
        }

    } while (true);

    int recordSize = 0;
    for (int i = 0; i < (int)recordingList.size(); i++)
    {
        auto track = recordingList[i]["track"].get<vector<json>>();
        time_t dataFrom;
        time_t dataTo;
        bool addFlag = false;
        for (int ii = 0; ii < (int)track.size(); ii++)
        {
            if (track[ii]["trackTpye"] == "video")
            {
                dataFrom = utils.dateTimeToTime_t(track[ii]["dataFrom"].get<string>());
                dataTo = utils.dateTimeToTime_t(track[ii]["dataTo"].get<string>());

                if (requestEndTime < dataFrom || requestBeginTime > dataTo)
                {
                    addFlag = false;
                }
                else
                {
                    addFlag = true;
                }
                break;
            }
        }
        if (addFlag)
        {
            if (recordSize >= (int)inBuffer->byIndex * 20 + 20)
            {
                recordSize++;
                continue;
            }
            if (recordSize >= (int)inBuffer->byIndex * 20)
            {
                strcpy(outBuffer->szStartTime[recordSize - (int)inBuffer->byIndex * 20], utils.time_tToLocalTime(dataFrom).c_str());
                strcpy(outBuffer->szStopTime[recordSize - (int)inBuffer->byIndex * 20], utils.time_tToLocalTime(dataTo).c_str());
                strcpy(outBuffer->szRecordName[recordSize - (int)inBuffer->byIndex * 20], recordingList[i]["recordingToken"].get<string>().c_str());
            }
            recordSize++;
        }
    }

    if (recordSize <= (int)inBuffer->byIndex * 20 + 20)
    {
        if (recordSize - (int)inBuffer->byIndex * 20 < 0)
        {
            outBuffer->byListSize = 0;
        }
        else
        {
            outBuffer->byListSize = recordSize - (int)inBuffer->byIndex * 20;
        }
        outBuffer->byMore = 0;
    }
    else
    {
        outBuffer->byListSize = 20;
        outBuffer->byMore = 1;
    }

    return true;
}

// 通过命令码获取回放URI
bool onvifService::getRecordURI(string serviceAddr, ONVIFTOOL_GET_RECORD_URI_PARAM *inBuffer,
                                ONVIFTOOL_RET_GET_URI_PARAM *outBuffer)
{
    log("call getRecordURI: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getRecordURI fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return false;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return false;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 获取服务地址
    string relayService = "";
    // 获取媒体服务地址
    getServices(serviceAddr, "replay_service", relayService);

    string responseJson = "";
    if (relayService == "")
    {
        log(makeErrStr(errPrefix, string("device: ") + serviceAddr + " not suppose replay_service"), LOG_ERROR);
        return false;
    }

    ONVIF_STREAM_SETUP_PARAM streamSetup;
    streamSetup.transport.tunnel = nullptr;
    switch (inBuffer->byTransportType)
    {
    case 0:
        streamSetup.streamType = 0;
        streamSetup.transport.protocolsName = 0;
        break;
    case 1:
        streamSetup.streamType = 1;
        streamSetup.transport.protocolsName = 0;
        break;
    case 2:
        streamSetup.streamType = 0;
        streamSetup.transport.protocolsName = 2;
        break;
    case 3:
        streamSetup.streamType = 0;
        streamSetup.transport.protocolsName = 0;
        break;
    case 4:
        streamSetup.streamType = 1;
        streamSetup.transport.protocolsName = 0;
        break;
    case 5:
        streamSetup.streamType = 0;
        streamSetup.transport.protocolsName = 3;
        break;

    default:
        break;
    }

    if (auto err = onvifTool_->getReplayUri(responseJson, relayService.c_str(), userName.c_str(),
                                            password.c_str(), inBuffer->szRecordName, &streamSetup);
        err)
    {
        log(makeErrStr(errPrefix, *err), LOG_ERROR);
        return false;
    }

    // 将URI写入outBuffer
    json j = json::parse(responseJson);
    auto URI = j["uri"].get<string>();
    string newURI = "";

    // 为URI附带上用户名和密码
    if (URI.substr(0, 4) == "rtsp")
    {
        newURI = URI.substr(0, 7) + userName + string(":") + password + string("@") + URI.substr(7);
    }
    else
    {
        newURI = URI;
    }
    memset(outBuffer->URI, 0, sizeof(outBuffer->URI));
    strcpy(outBuffer->URI, newURI.c_str());
    return true;
}

// 通过命令码获取预置位信息
bool onvifService::getPresets(string serviceAddr, ONVIFTOOL_PRESET_PARAM *inBuffer,
                              ONVIFTOOL_RET_PRESET_PARAM *outBuffer)
{
    log("call getPresets: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "getPresets fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return false;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return false;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 获取服务地址
    string ptzService = "";
    // 获取媒体服务地址
    getServices(serviceAddr, "ptz_service", ptzService);

    string videoSourceConfigToken = "";
    videoSourceConfigToken = getVideoSourceConfigToken(serviceAddr, (int)inBuffer->dwChannelNum);
    if (videoSourceConfigToken == "")
    {
        string errStr = serviceAddr + string(", channelNum: ") +
                        to_string((int)inBuffer->dwChannelNum) +
                        string(", has no video source");
        log(errStr, LOG_ERROR);
        return false;
    }

    auto profileToken = getProfileTokenList(serviceAddr, videoSourceConfigToken);
    if (profileToken.size() == 0)
    {
        string errStr = serviceAddr + string(", channelNum: ") +
                        to_string((int)inBuffer->dwChannelNum) +
                        string(", no stream found");
        log(errStr, LOG_ERROR);
        return false;
    }

    string responseJson = "";
    if (ptzService == "")
    {
        log(makeErrStr(errPrefix, string("device: ") + serviceAddr + " not suppose ptz_service"), LOG_ERROR);
        return false;
    }

    // 查询预置位
    if (auto err = onvifTool_->getPresets(responseJson, ptzService.c_str(),
                                          userName.c_str(), password.c_str(), profileToken[0].c_str());
        err)
    {
        log(makeErrStr(errPrefix, *err), LOG_ERROR);
        return false;
    }

    // 解析返回内容
    memset(outBuffer, 0, sizeof(ONVIFTOOL_RET_PRESET_PARAM));
    json j = json::parse(responseJson);
    if (j["count"] == 0)
    {
        outBuffer->dwListSize = 0;
        outBuffer->byMore = 0;
        return true;
    }

    vector<json> presets = j["preset"].get<vector<json>>();

    // 保存
    int size = 0;
    for (int i = 0; i < (int)presets.size(); i++)
    {
        if (presets[i].find("PTZPosition") != presets[i].end() &&
            presets[i]["PTZPosition"].find("panTilt") != presets[i]["PTZPosition"].end())
        {
            string position = presets[i]["PTZPosition"]["panTilt"]["x"].get<string>() +
                              presets[i]["PTZPosition"]["panTilt"]["y"].get<string>();
            if (size >= (int)inBuffer->byIndex * 300 + 300)
            {
                size++;
                continue;
            }
            if (size >= (int)inBuffer->byIndex * 300)
            {
                outBuffer->dwPresetIndex[size] = stoi(presets[i]["token"].get<string>());
                strcpy(outBuffer->szPresetName[size], presets[i]["name"].get<string>().c_str());
            }
            size++;
        }
    }

    if (size <= (int)inBuffer->byIndex * 300 + 300)
    {
        if (size - (int)inBuffer->byIndex * 300 < 0)
        {
            outBuffer->dwListSize = 0;
        }
        else
        {
            outBuffer->dwListSize = size - (int)inBuffer->byIndex * 300;
        }
        outBuffer->byMore = 0;
    }
    else
    {
        outBuffer->dwListSize = 300;
        outBuffer->byMore = 1;
    }

    return true;
}

// 通过命令码设置通道名
bool onvifService::setChannelName(string serviceAddr, ONVIFTOOL_SET_CHANNEL_PARAM *inBuffer)
{
    log("call setChannelName: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "setChannelName fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return false;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return false;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 获取服务地址
    string media2Service = "";
    string mediaService = "";
    // 获取媒体服务地址
    getServices(serviceAddr, "media2_service", media2Service);
    getServices(serviceAddr, "media_service", mediaService);

    // 发起请求获取通道对应的视频源
    string responseJson = "";
    if (media2Service != "")
    {
        log("media2_service", LOG_DEBUG);
        if (auto err = onvifTool_->getOSDs_V2(responseJson, media2Service.c_str(), userName.c_str(), password.c_str()); err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
    }
    else if (mediaService != "")
    {
        log("media_service", LOG_DEBUG);
        if (auto err = onvifTool_->getOSDs_V1(responseJson, mediaService.c_str(), userName.c_str(), password.c_str()); err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
    }
    else
    {
        log(makeErrStr(errPrefix, string("device: ") + serviceAddr + " not suppose media_service and media2_service"), LOG_ERROR);
        return false;
    }

    // 解析返回内容获取channel对应的OSD
    string videoSourceConfigurationToken = "";
    ONVIF_SET_OSD_PARAM param; // 修改OSD的参数
    float x;
    float y;
    json j = json::parse(responseJson);
    auto OSDs = j["OSDs"].get<vector<json>>();
    unordered_map<string, string> sourceChannelMap; // map<source, channelName>
    vector<string> channelNameVec;                  // 按顺序保存的channelName
    for (int i = 0; i < (int)OSDs.size(); i++)
    {

        if (OSDs[i].find("videoSourceConfigurationToken") == OSDs[i].end() || OSDs[i]["videoSourceConfigurationToken"] == "")
        {
            continue;
        }
        if (OSDs[i].find("type") == OSDs[i].end() || OSDs[i]["type"] != "text")
        {
            continue;
        }
        if (OSDs[i].find("textString") == OSDs[i].end() ||
            OSDs[i]["textString"].find("type") == OSDs[i]["textString"].end() ||
            OSDs[i]["textString"]["type"] != "Plain")
        {
            continue;
        }
        if (OSDs[i]["textString"].find("plainText") == OSDs[i]["textString"].end() ||
            OSDs[i]["textString"]["plainText"] == "")
        {
            continue;
        }
        videoSourceConfigurationToken = OSDs[i]["videoSourceConfigurationToken"].get<string>();
        auto channelName = OSDs[i]["textString"]["plainText"].get<string>();

        // 视频源第一个文本OSD为通道名
        if (sourceChannelMap.find(videoSourceConfigurationToken) == sourceChannelMap.end())
        {
            sourceChannelMap[videoSourceConfigurationToken] = channelName;

            // 按顺序保存通道名
            channelNameVec.push_back(channelName);
            // 当前通道号是选择的通道号
            if ((int)channelNameVec.size() == (int)inBuffer->dwChannelNum)
            {
                // 设置请求参数
                param.OSDConfigurationToken = (char *)OSDs[i]["token"].get<string>().c_str();
                param.videoSourceConfigurationToken = (char *)videoSourceConfigurationToken.c_str();
                param.type = 0;
                param.positionType = 4;
                x = stof(OSDs[i]["position"]["pos"]["x"].get<string>().c_str());
                y = stof(OSDs[i]["position"]["pos"]["y"].get<string>().c_str());
                param.positionX = &x;
                param.positionY = &y;
                param.isPersistentText = nullptr;
                param.textType = 0;
                param.dateFormat = 0;
                param.timeFormat = 0;
                param.fontSize = nullptr;
                param.fontColor = nullptr;
                param.fontTransparent = nullptr;
                param.backgroundColor = nullptr;
                param.backgroundTransparent = nullptr;
                param.plainText = inBuffer->szChannelName;
                param.imgPath = nullptr;

                break;
            }
        }
        videoSourceConfigurationToken = "";
    }

    // 发起修改请求
    if (media2Service != "")
    {
        if (auto err = onvifTool_->setOSD_V2(responseJson, media2Service.c_str(), userName.c_str(), password.c_str(), &param); err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
    }
    else if (mediaService != "")
    {
        if (auto err = onvifTool_->setOSD_V1(responseJson, mediaService.c_str(), userName.c_str(), password.c_str(), &param); err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
    }

    return true;
}

// 通过命令码重启设备
bool onvifService::setRebootDevice(string serviceAddr)
{
    log("setRebootDevice: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "setRebootDevice fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return false;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return false;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 发起重启请求
    string responseJson;
    if (auto err = onvifTool_->systemReboot(responseJson, serviceAddr.c_str(), userName.c_str(), password.c_str()); err)
    {
        log(makeErrStr(errPrefix, *err), LOG_ERROR);
        return false;
    }
    log(serviceAddr + string(" reboot success, response: ") + responseJson, LOG_INFO);
    return true;
}

// 通过命令码PTZ控制
bool onvifService::setPTZ(string serviceAddr, ONVIFTOOL_SET_PTZ_PARAM *inBuffer)
{
    log("call setPTZ: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "setPTZ fail";
    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return false;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return false;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 获取服务地址
    string ptzService = "";
    string imagingService = "";
    string provisioningService = "";
    // 获取媒体服务地址
    getServices(serviceAddr, "ptz_service", ptzService);
    getServices(serviceAddr, "imaging_service", imagingService);
    getServices(serviceAddr, "provisioning_service", provisioningService);
    if (ptzService == "")
    {
        log(makeErrStr(errPrefix, string("device: ") + serviceAddr + " not suppose ptz_service"), LOG_ERROR);
        return false;
    }

    string videoSourceConfigToken = "";
    videoSourceConfigToken = getVideoSourceConfigToken(serviceAddr, (int)inBuffer->dwChannelNum);
    if (videoSourceConfigToken == "")
    {
        string errStr = serviceAddr + string(", channelNum: ") +
                        to_string((int)inBuffer->dwChannelNum) +
                        string(", has no video source");
        log(errStr, LOG_ERROR);
        return false;
    }

    string videoSourceToken = "";
    videoSourceToken = getVideoSourceToken(serviceAddr, videoSourceConfigToken);

    auto profileToken = getProfileTokenList(serviceAddr, videoSourceConfigToken);
    if (profileToken.size() == 0)
    {
        string errStr = serviceAddr + string(", channelNum: ") +
                        to_string((int)inBuffer->dwChannelNum) +
                        string(", no stream found");
        log(errStr, LOG_ERROR);
        return false;
    }

    string responseJson = "";
    if (inBuffer->byAction != 0)
    {
        log("PTZ control, action: stop", LOG_INFO);
        // 停止PTZ
        if (auto err = onvifTool_->stopMove(responseJson, ptzService.c_str(),
                                            userName.c_str(), password.c_str(), profileToken[0].c_str());
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }

        if (provisioningService != "")
        {
            if (auto err = onvifTool_->stopFocusMove(responseJson, provisioningService.c_str(),
                                                     userName.c_str(), password.c_str(), videoSourceToken.c_str());
                err)
            {
                log(makeErrStr(errPrefix, *err), LOG_ERROR);
                return false;
            }
        }
        else if (imagingService != "")
        {
            if (auto err = onvifTool_->stopFocusMove_Imaging(responseJson, imagingService.c_str(),
                                                             userName.c_str(), password.c_str(), videoSourceToken.c_str());
                err)
            {
                log(makeErrStr(errPrefix, *err), LOG_ERROR);
                return false;
            }
        }
        return true;
    }

    // 设置PTZ参数
    bool isFocus = false;
    float moveSpeed = 0;
    ONVIF_PTZ_SPEED_PARAM speed = {0};
    speed.panTiltSpeedEnable = false;
    speed.panTiltSpace = 2;
    speed.panSpeed = 0;
    speed.tiltSpeed = 0;
    speed.zoomSpeedEnable = false;
    speed.zoomSpace = 2;
    speed.zoomSpeed = 0;
    if (inBuffer->speed > 1 || inBuffer->speed <= 0)
    {
        moveSpeed = 1;
    }
    else
    {
        moveSpeed = inBuffer->speed;
    }

    switch (inBuffer->byCommand)
    {
    case 0:
        log("PTZ control, command: left", LOG_INFO);
        speed.panTiltSpeedEnable = true;
        speed.panSpeed = -moveSpeed;
        break;
    case 1:
        log("PTZ control, command: right", LOG_INFO);
        speed.panTiltSpeedEnable = true;
        speed.panSpeed = moveSpeed;
        break;
    case 2:
        log("PTZ control, command: up", LOG_INFO);
        speed.panTiltSpeedEnable = true;
        speed.tiltSpeed = moveSpeed;
        break;
    case 3:
        log("PTZ control, command: down", LOG_INFO);
        speed.panTiltSpeedEnable = true;
        speed.tiltSpeed = -moveSpeed;
        break;
    case 4:
        log("PTZ control, command: left_up", LOG_INFO);
        speed.panTiltSpeedEnable = true;
        speed.panSpeed = -moveSpeed;
        speed.tiltSpeed = moveSpeed;
        break;
    case 5:
        log("PTZ control, command: left_down", LOG_INFO);
        speed.panTiltSpeedEnable = true;
        speed.panSpeed = -moveSpeed;
        speed.tiltSpeed = -moveSpeed;
        break;
    case 6:
        log("PTZ control, command: right_up", LOG_INFO);
        speed.panTiltSpeedEnable = true;
        speed.panSpeed = moveSpeed;
        speed.tiltSpeed = moveSpeed;
        break;
    case 7:
        log("PTZ control, command: right_down", LOG_INFO);
        speed.panTiltSpeedEnable = true;
        speed.panSpeed = moveSpeed;
        speed.tiltSpeed = -moveSpeed;
        break;
    case 8:
        log("PTZ control, command: zoom_in", LOG_INFO);
        speed.zoomSpeedEnable = true;
        speed.zoomSpeed = moveSpeed;
        break;
    case 9:
        log("PTZ control, command: zoom_out", LOG_INFO);
        speed.zoomSpeedEnable = true;
        speed.zoomSpeed = -moveSpeed;
        break;
    case 10:
        log("PTZ control, command: focus_near", LOG_INFO);
        isFocus = true;
        moveSpeed = -moveSpeed;
        break;
    case 11:
        log("PTZ control, command: focus_far", LOG_INFO);
        isFocus = true;
        break;

    default:
        break;
    }

    if (isFocus)
    {
        if (provisioningService != "")
        {
            ONVIF_FOCUS_MOVE_PARAM moveParam;
            moveParam.timeOut = (char *)"PT60S";
            if (moveSpeed < 0)
            {
                moveParam.direction = 1;
            }
            else
            {
                moveParam.direction = 2;
            }

            if (auto err = onvifTool_->focusMove(responseJson, provisioningService.c_str(),
                                                 userName.c_str(), password.c_str(),
                                                 videoSourceToken.c_str(), &moveParam);
                err)
            {
                log(makeErrStr(errPrefix, *err), LOG_ERROR);
                return false;
            }
        }
        else if (imagingService != "")
        {

            if (auto err = onvifTool_->focusMove_Imaging(responseJson, imagingService.c_str(),
                                                         userName.c_str(), password.c_str(),
                                                         videoSourceToken.c_str(), moveSpeed);
                err)
            {
                log(makeErrStr(errPrefix, *err), LOG_ERROR);
                return false;
            }
        }
        else
        {
            log(makeErrStr(errPrefix, string("device: ") + serviceAddr + " not suppose imaging_service and provisioning_service"), LOG_ERROR);
            return false;
        }
    }
    else
    {

        if (auto err = onvifTool_->continuousMove(responseJson, ptzService.c_str(),
                                                  userName.c_str(), password.c_str(),
                                                  profileToken[0].c_str(), &speed, "PT60S");
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
    }
    log("PTZ control, start PTZ", LOG_INFO);

    return true;
}

// 通过命令码设置预置位
bool onvifService::setPresets(string serviceAddr, ONVIFTOOL_PRESET_PARAM *inBuffer,
                              ONVIFTOOL_RET_PRESET_PARAM *outBuffer)
{
    log("call setPresets: " + string(serviceAddr), LOG_DEBUG);
    string errPrefix = "setPresets fail";

    if (inBuffer->byIndex > 0)
    {
        log("setPresets param: byIndex > 0, begin get remain presets", LOG_INFO);
        if (getPresets(serviceAddr, inBuffer, outBuffer) == false)
        {
            log(makeErrStr(errPrefix, "byIndex > 0: call getPresets fail"), LOG_ERROR);
            return false;
        }
        return true;
    }

    // 获取鉴权所需参数
    auto val = addrHandleMap_.findLeft(serviceAddr);
    if (val == nullopt)
    {
        log(makeErrStr(errPrefix, "serviceAddr: " + serviceAddr + " is not found"), LOG_ERROR);
        return false;
    }
    auto handle = *val;
    auto [userInfo, error_] = handleUserMap_.find(handle);
    if (error_ != "")
    {
        log(makeErrStr(errPrefix, "unknown error"), LOG_ERROR);
        return false;
    }
    auto userName = (*userInfo)[0];
    auto password = (*userInfo)[1];

    // 获取服务地址
    string ptzService = "";
    // 获取媒体服务地址
    getServices(serviceAddr, "ptz_service", ptzService);

    string videoSourceConfigToken = "";
    videoSourceConfigToken = getVideoSourceConfigToken(serviceAddr, (int)inBuffer->dwChannelNum);
    if (videoSourceConfigToken == "")
    {
        string errStr = serviceAddr + string(", channelNum: ") +
                        to_string((int)inBuffer->dwChannelNum) +
                        string(", has no video source");
        log(errStr, LOG_ERROR);
        return false;
    }

    auto profileToken = getProfileTokenList(serviceAddr, videoSourceConfigToken);
    if (profileToken.size() == 0)
    {
        string errStr = serviceAddr + string(", channelNum: ") +
                        to_string((int)inBuffer->dwChannelNum) +
                        string(", no stream found");
        log(errStr, LOG_ERROR);
        return false;
    }

    string responseJson = "";
    if (ptzService == "")
    {
        log(makeErrStr(errPrefix, string("device: ") + serviceAddr + " not suppose ptz_service"), LOG_ERROR);
        return false;
    }

    // 解析返回内容
    memset(outBuffer, 0, sizeof(ONVIFTOOL_RET_PRESET_PARAM));

    string presetToken = to_string(inBuffer->dwPresetIndex);

    switch (inBuffer->byCommand)
    {
    case 1:
    {
        log(string("add preset, presetIndex: ") + presetToken + ", presetName: " + string(inBuffer->szPresetName), LOG_INFO);
        if (auto err = onvifTool_->setPreset(responseJson, ptzService.c_str(),
                                             userName.c_str(), password.c_str(),
                                             profileToken[0].c_str(), presetToken.c_str(), inBuffer->szPresetName);
            !err)
        {
            log(string("add preset with specified presetIndex: ") + presetToken + " success", LOG_INFO);
            if (getPresets(serviceAddr, inBuffer, outBuffer) == false)
            {
                log(makeErrStr(errPrefix, "call getPresets fail"), LOG_ERROR);
                return false;
            }
            return true;
        }
        else
        {
            log(makeErrStr(errPrefix, *err), LOG_WARNING);
        }

        // 不支持指定presetToken添加，尝试直接添加
        log(string("add preset with specified presetIndex: ") + presetToken + " fail", LOG_INFO);
        if (auto err = onvifTool_->addPreset(responseJson, ptzService.c_str(),
                                             userName.c_str(), password.c_str(),
                                             profileToken[0].c_str(), inBuffer->szPresetName);
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_WARNING);
            return false;
        }
        json j = json::parse(responseJson);

        log(string("new presetIndex: ") + j["presetToken"].get<string>(), LOG_INFO);
    }
    break;
    case 2:
    {
        log(string("delete preset, presetIndex: ") + presetToken, LOG_INFO);
        if (auto err = onvifTool_->removePreset(responseJson, ptzService.c_str(),
                                                userName.c_str(), password.c_str(),
                                                profileToken[0].c_str(), presetToken.c_str());
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
    }
    break;
    case 3:
    {
        log(string("edit preset, presetIndex: ") + presetToken + ", presetName: " + string(inBuffer->szPresetName), LOG_INFO);
        if (auto err = onvifTool_->setPreset(responseJson, ptzService.c_str(),
                                             userName.c_str(), password.c_str(),
                                             profileToken[0].c_str(), presetToken.c_str(), inBuffer->szPresetName);
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
    }
    break;
    case 4:
    {
        log(string("goto preset, presetIndex: ") + presetToken, LOG_INFO);
        if (auto err = onvifTool_->gotoPreset(responseJson, ptzService.c_str(),
                                              userName.c_str(), password.c_str(),
                                              profileToken[0].c_str(), presetToken.c_str());
            err)
        {
            log(makeErrStr(errPrefix, *err), LOG_ERROR);
            return false;
        }
    }
    break;

    default:
        break;
    }

    // 获取所有预置位的信息
    if (getPresets(serviceAddr, inBuffer, outBuffer) == false)
    {
        log(makeErrStr(errPrefix, "call getPresets fail"), LOG_ERROR);
        return false;
    }
    return true;
}

onvifService *service = nullptr;
mutex onvifMutex;

unsigned int ONVIFTOOL_GetVersion()
{
    return onvifService::getVersion();
}

BOOL ONVIFTOOL_SetLogToFile(int nLogLevel, char const *strLogDir, int bAutoDel)
{
    onvifMutex.lock();
    if (service == nullptr)
    {
        onvifMutex.unlock();
        return false;
    }
    auto result = service->setLogToFile(nLogLevel, strLogDir, bAutoDel);
    onvifMutex.unlock();
    return result;
}

BOOL ONVIFTOOL_Start(PDEVICE_FIND_CALLBACK pDeviceFindCallBack, int bInstallNPF, void *pUserData)
{
    onvifMutex.lock();
    if (service == nullptr)
    {
        service = new onvifService();
        auto result = service->start(pDeviceFindCallBack, bInstallNPF, pUserData);

        if (result == false)
        {
            delete service;
            service = nullptr;
        }

        onvifMutex.unlock();
        return result;
    }
    onvifMutex.unlock();
    return true;
}

BOOL ONVIFTOOL_Stop()
{
    onvifMutex.lock();
    if (service == nullptr)
    {
        onvifMutex.unlock();
        return false;
    }
    auto result = service->stop();
    delete service;
    service = nullptr;
    onvifMutex.unlock();
    return result;
}

BOOL ONVIFTOOL_Login(const char *serviceAddr, const char *userName, const char *password,
                     char *handleOutBuffer, unsigned int outBufferSize)
{
    onvifMutex.lock();
    if (service == nullptr)
    {
        onvifMutex.unlock();
        return false;
    }
    auto result = service->login(serviceAddr, userName, password, handleOutBuffer, outBufferSize);
    onvifMutex.unlock();
    return result;
}

BOOL ONVIFTOOL_SendInquery()
{
    onvifMutex.lock();
    if (service == nullptr)
    {
        onvifMutex.unlock();
        return false;
    }
    auto result = service->sendInquery();
    onvifMutex.unlock();
    return result;
}

void ONVIFTOOL_SetAutoRequestInterval(unsigned int dwInterval)
{
    onvifMutex.lock();
    if (service == nullptr)
    {
        onvifMutex.unlock();
        return;
    }
    service->setAutoRequestInterval(dwInterval);
    onvifMutex.unlock();
    return;
}

BOOL ONVIFTOOL_ModifyDeviceNetParam(const char *handle, const ONVIFTOOL_DEV_NET_PARAM *lpNetParam,
                                    ONVIFTOOL_DEV_RET_NET_PARAM *lpRetNetParam, unsigned int dwOutBuffSize)
{
    onvifMutex.lock();
    if (service == nullptr)
    {
        onvifMutex.unlock();
        return false;
    }

    auto result = service->modifyDeviceNetParam(handle, lpNetParam, lpRetNetParam, dwOutBuffSize);
    onvifMutex.unlock();
    return result;
}

unsigned int ONVIFTOOL_GetLastError()
{
    onvifMutex.lock();
    if (service == nullptr)
    {
        onvifMutex.unlock();
        return 0;
    }
    auto result = service->getLastError();
    onvifMutex.unlock();
    return result;
}

BOOL ONVIFTOOL_Clearup()
{
    onvifMutex.lock();
    if (service == nullptr)
    {
        onvifMutex.unlock();
        return false;
    }

    auto result = service->clearup();
    onvifMutex.unlock();
    return result;
}

BOOL ONVIFTOOL_GetDeviceConfig(const char *handle, unsigned int dwCommand,
                               void *lpInBuffer, unsigned int dwInBuffSize,
                               void *lpOutBuffer, unsigned int dwOutBuffSize)
{
    onvifMutex.lock();
    if (service == nullptr)
    {
        onvifMutex.unlock();
        return false;
    }
    auto result = service->getDeviceConfig(handle, dwCommand, lpInBuffer, dwInBuffSize, lpOutBuffer, dwOutBuffSize);
    onvifMutex.unlock();
    return result;
}

BOOL ONVIFTOOL_SetDeviceConfig(const char *handle, unsigned int dwCommand,
                               void *lpInBuffer, unsigned int dwInBuffSize,
                               void *lpOutBuffer, unsigned int dwOutBuffSize)
{
    onvifMutex.lock();
    if (service == nullptr)
    {
        onvifMutex.unlock();
        return false;
    }
    auto result = service->setDeviceConfig(handle, dwCommand, lpInBuffer, dwInBuffSize, lpOutBuffer, dwOutBuffSize);
    onvifMutex.unlock();
    return result;
}

BOOL ONVIFTOOL_ResetPasswd(const char *handle,
                           const ONVIFTOOL_RESET_PARAM *pResetParam,
                           ONVIFTOOL_RET_RESET_PARAM *pRetResetParam)
{
    onvifMutex.lock();
    if (service == nullptr)
    {
        onvifMutex.unlock();
        return false;
    }
    auto result = service->resetPasswd(handle, pResetParam, pRetResetParam);
    onvifMutex.unlock();
    return result;
}